Coverage Report

Created: 2026-02-05 09:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\dns\packet\authority\mod.rs
Line
Count
Source
1
use crate::dns::q_class::DNSClass;
2
use crate::dns::q_name::parse_qname;
3
use crate::dns::q_type::DNSRecordType;
4
use crate::exceptions::SCloudException;
5
6
#[derive(PartialEq, Debug)]
7
pub(crate) struct AuthoritySection {
8
    pub(crate) q_name: String,
9
    pub(crate) q_type: DNSRecordType,
10
    pub(crate) q_class: DNSClass,
11
    pub(crate) ttl: u32,
12
    pub(crate) ns_name: String,
13
}
14
15
impl AuthoritySection {
16
    /// Deserialize one AuthoritySection (NS record) and return (section, consumed_bytes)
17
    ///
18
    /// # Exemple :
19
    /// ```
20
    /// use crate::dns::packet::authority::AuthoritySection;
21
    /// use crate::dns::q_type::DNSRecordType;
22
    /// use crate::dns::q_class::DNSClass;
23
    ///
24
    /// // example.com NS ns1.example.com
25
    /// let raw_authority: Vec<u8> = vec![
26
    ///     0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e',
27
    ///     0x03, b'c', b'o', b'm',
28
    ///     0x00,             // End of QNAME
29
    ///     0x00, 0x02,       // TYPE = NS
30
    ///     0x00, 0x01,       // CLASS = IN
31
    ///     0x00, 0x00, 0x0e, 0x10, // TTL = 3600
32
    ///     0x00, 0x11,       // RDLENGTH = 17
33
    ///     0x03, b'n', b's', b'1',
34
    ///     0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e',
35
    ///     0x03, b'c', b'o', b'm',
36
    ///     0x00,             // End of NSNAME
37
    /// ];
38
    ///
39
    /// let (authority, consumed) =
40
    ///     AuthoritySection::from_bytes(&raw_authority, 0).unwrap();
41
    ///
42
    /// assert_eq!(authority.q_name, "example.com");
43
    /// assert_eq!(authority.q_type, DNSRecordType::NS);
44
    /// assert_eq!(authority.q_class, DNSClass::IN);
45
    /// assert_eq!(authority.ttl, 3600);
46
    /// assert_eq!(authority.ns_name, "ns1.example.com");
47
    /// assert_eq!(consumed, raw_authority.len());
48
    /// ```
49
5
    pub(crate) fn from_bytes(
50
5
        buf: &[u8],
51
5
        offset: usize,
52
5
    ) -> Result<(AuthoritySection, usize), SCloudException> {
53
5
        let (q_name, mut pos) = parse_qname(buf, offset).unwrap();
54
55
5
        if buf.len() < pos + 10 {
56
2
            return Err(SCloudException::SCLOUD_AUTHORITY_DESERIALIZATION_FAILED_BUF_TOO_SHORT);
57
3
        }
58
59
3
        let q_type = DNSRecordType::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap();
60
3
        pos += 2;
61
62
3
        let q_class = DNSClass::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap();
63
3
        pos += 2;
64
65
3
        let ttl = u32::from_be_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
66
3
        pos += 4;
67
68
3
        let rdlength = u16::from_be_bytes([buf[pos], buf[pos + 1]]);
69
3
        pos += 2;
70
71
3
        if buf.len() < pos + rdlength as usize {
72
1
            return Err(
73
1
                SCloudException::SCLOUD_AUTHORITY_DESERIALIZATION_FAILED_RDATA_OUT_OF_BOUNDS,
74
1
            );
75
2
        }
76
77
2
        let (ns_name, _) = parse_qname(buf, pos).unwrap();
78
79
2
        pos += rdlength as usize;
80
81
2
        Ok((
82
2
            AuthoritySection {
83
2
                q_name,
84
2
                q_type,
85
2
                q_class,
86
2
                ttl,
87
2
                ns_name,
88
2
            },
89
2
            pos - offset,
90
2
        ))
91
5
    }
92
93
    /// Serialize the AuthoritySection (NS record) into bytes
94
    ///
95
    /// # Exemple :
96
    /// ```
97
    /// use crate::dns::packet::authority::AuthoritySection;
98
    /// use crate::dns::q_type::DNSRecordType;
99
    /// use crate::dns::q_class::DNSClass;
100
    ///
101
    /// let authority = AuthoritySection {
102
    ///     q_name: "example.com".to_string(),
103
    ///     q_type: DNSRecordType::NS,
104
    ///     q_class: DNSClass::IN,
105
    ///     ttl: 3600,
106
    ///     ns_name: "ns1.example.com".to_string(),
107
    /// };
108
    ///
109
    /// let bytes = authority.to_bytes().unwrap();
110
    ///
111
    /// // NAME + TYPE + CLASS + TTL + RDLENGTH + RDATA
112
    /// assert!(bytes.len() > 20);
113
    /// ```
114
1
    pub(crate) fn to_bytes(&self) -> Result<Vec<u8>, SCloudException> {
115
1
        let mut buf = Vec::new();
116
117
3
        for label in 
self.q_name.split('.')1
{
118
3
            buf.push(label.len() as u8);
119
3
            buf.extend_from_slice(label.as_bytes());
120
3
        }
121
1
        buf.push(0x00);
122
123
1
        let qtype_u16 =
124
1
            u16::try_from(self.q_type).expect("Cannot convert AuthoritySection q_type to u16");
125
1
        buf.extend_from_slice(&qtype_u16.to_be_bytes());
126
127
1
        let qclass_u16 = u16::try_from(self.q_class).unwrap();
128
1
        buf.extend_from_slice(&qclass_u16.to_be_bytes());
129
130
1
        buf.extend_from_slice(&self.ttl.to_be_bytes());
131
132
1
        let mut rdata = Vec::new();
133
4
        for label in 
self.ns_name.split('.')1
{
134
4
            rdata.push(label.len() as u8);
135
4
            rdata.extend_from_slice(label.as_bytes());
136
4
        }
137
1
        rdata.push(0x00);
138
139
1
        buf.extend_from_slice(&(rdata.len() as u16).to_be_bytes());
140
1
        buf.extend_from_slice(&rdata);
141
142
1
        Ok(buf)
143
1
    }
144
}